<?php
/*
 *---------------------------------------------------------------------------
 *
 *                  T E N C E N T   P R O P R I E T A R Y
 *
 *     COPYRIGHT (c)  2008 BY  TENCENT  CORPORATION.  ALL RIGHTS
 *     RESERVED.   NO  PART  OF THIS PROGRAM  OR  PUBLICATION  MAY
 *     BE  REPRODUCED,   TRANSMITTED,   TRANSCRIBED,   STORED  IN  A
 *     RETRIEVAL SYSTEM, OR TRANSLATED INTO ANY LANGUAGE OR COMPUTER
 *     LANGUAGE IN ANY FORM OR BY ANY MEANS, ELECTRONIC, MECHANICAL,
 *     MAGNETIC,  OPTICAL,  CHEMICAL, MANUAL, OR OTHERWISE,  WITHOUT
 *     THE PRIOR WRITTEN PERMISSION OF :
 *
 *                        TENCENT  CORPORATION
 *
 *       Advertising Platform R&D Team, Advertising Platform & Products
 *       Tencent Ltd.
 *---------------------------------------------------------------------------
 */

/**
 * Used to load all the class automatically
 *
 * @package lib.autoload
 * @author  Samon Ma <samonma@tencent.com>
 */
class JYAutoload{
    private
        $includeFile,
        $savePath,
        $saveName,
        $ignore,
        $dirs;

    public $autoloadPaths;

     /**
     *@var TMAutoload
     *@access private
     */
    private static $instance;

    protected function __construct(){
        spl_autoload_register(array('JYAutoload', '__autoload'));

        $this->initialize();
    }

    /**
     * Get a instance of TMAutoload
     * @access public
     * @param bool $newInstance   create a new instance or not
     * @return TMAutoload
     */
    public static function getInstance($newInstance = false)
    {
        if($newInstance || self::$instance == null)
        {
            self::$instance = new self();
        }

        return self::$instance;
    }

    private function initialize(){
        $this->autoloadPaths = array();
        $this->includeFile = array();
        $this->savePath = 'autoload/';        
        $this->saveName = 'autoloader.php';
        
        $this->ignore = array('.', '..', '.svn', '_config', "test", "template");
        $this->dirs = array(JYAPI_PATH);
    }

    /**
     * Customized __autoload function:Autoload the class which registered in autoload.php
     * @access public
     * @param string $className    the class name
     * @return void|string    the error information
     */
    public static function __autoload($className){
        $filePath = self::getInstance()->getClassPath($className);

        if($filePath)
        {
            include_once($filePath);
        }
        else
        {
            //self::getInstance()->initialize();
            self::getInstance()->execute(true);
            $filePath = self::getInstance()->getClassPath($className);
            if($filePath)
            {
                include_once($filePath);
            }
            else
            {
                echo 'Cant find class: '.$className;
            }
        }
    }

    /**
     * add dir to scan dirs dynamically
     * @access public
     * @param string $dir    the dir needs scan
     * @return void
     */
    public function addDir($dir) {
        array_push($this->dirs, $dir);
    }

    /**
     * create autoload.php and add customized __autoload function to spl__autoload stack
     * @access public
     * @param bool $reload Reload the autoload file if this parameter is true
     * @return void
     */
    public function execute($reload = false){

        if ($reload)
        {
            if (is_file(CACHE_PATH.$this->savePath.$this->saveName))
            {
				unlink(CACHE_PATH.$this->savePath.$this->saveName);
				//rename(CACHE_PATH.$this->savePath.$this->saveName,CACHE_PATH.$this->savePath.$this->saveName.'.del');
            }
        }

        if (!is_file(CACHE_PATH.$this->savePath.$this->saveName))
        {
            foreach ($this->dirs as $dir) {
                $this->opendir($dir);
            }

            $this->commit();
            $this->autoloadPaths = include(CACHE_PATH . $this->savePath . $this->saveName);
        }
    }

    /**
     * Used to get the path for a unknow class
     * @access public
     * @param string $className the class name
     * @return string|false
     */
    public function getClassPath($className)
    {
        if (!$this->autoloadPaths)
        {
            if (!is_file(CACHE_PATH . $this->savePath . $this->saveName))
            {
	
                $this->execute();
            }
        }
	
        $this->autoloadPaths = include(CACHE_PATH . $this->savePath . $this->saveName);
	
        if (isset($this->autoloadPaths[$className]))
        {
            return $this->autoloadPaths[$className];
        }
        else
        {
            return false;
        }
    }

    /**
     * Used to save the autoload file:autoload.php
     * @access private
     * @return void
     */
    private function commit()
    {
        foreach ($this->includeFile as $key => $file)
        {
            if (preg_match('/\.class\.php$/', $key))
            {
                $className = preg_replace('/\.class\.php/', '', $key);
                $files[$className] = is_array($file) ? $file[0] : $file;
            }
        }

        $path = $this->savePath;
        $name = $this->saveName;
        $content = "<?php\nreturn ".var_export($files, true).';';

        include_once JYAPI_PATH.'cache/JYFile.class.php';
		include_once JYAPI_PATH.'cache/JYFileCache.class.php';

        JYFileCache::set($path.$name, $content);
    }

    /**
     * Open dir and save to property
     * @access private
     * @param string $dir the dir needs scan
     * @return void
     */
    private function opendir($dir)
    {
        $handle = opendir($dir);
        while (false !== ($file = readdir($handle)))
        {
            if (!in_array($file, $this->ignore))
            {
                if (is_file($dir.$file))
                {
                    if (isset($this->includeFile[$file])) {//处理不同文件夹下具有相同文件名的类文件 存成数组
                        // the third one
                        // do nothing here
                    }
                    else
                    {
                        $this->includeFile[$file] = $dir.$file;
                    }
                }
                else
                {
                    $this->opendir($dir.$file.'/');
                }
            }
        }
    }
}